context
昨天遇到问题,当我的 NavitgationController back 的时候,程序崩溃,诡异的地方是: 控制台完全没有任何打印,崩溃的断点直接回到了 main 函数中.
几个尝试
想到之前曾经读过一篇出现在这样情景崩溃的文章:有对象依赖被 pop 的控制器,结果导致了崩溃,但是经过仔细排查,发现并没有这样的情况.
查找资料
网上对崩溃问题的定位常规方法总结起来有:
1.添加通用断点
- 选择 BreakPoint Navigator,点击右下角的 ‘+’ ,然后在弹窗中选择 ‘Add Exception BreakPoint’
- 右键断点,选择 ‘Edit BreakPoint’,检查设置如下
然后运行程序,程序就能定位到出现崩溃的代码.
但是很遗憾的是,这种方式只能解决大部分的问题(比如不识别的selector 等),有很多类型的崩溃它是不能定位的
比如出现 EXEC_BAD_ACCESS
这种错误,以上的方法是不能定位的.
2.重写object的respondsToSelector方法
1.重写object的respondsToSelector方法,现实出现EXEC_BAD_ACCESS前访问的最后一个object.因为有时候程序崩溃根本不知错误发生在什么地方。
在可能出现问题的 .m 或者.mm 文件中加入以下代码
1 | #ifdef _FOR_DEBUG_ |
- 在
other c flags
中加入-D _FOR_DEBUG_
(记住请只在Debug Configuration
下加入此标记),这样当你程序崩溃时,Xcode的console上就会准确地记录了最后运行的object的方法。
很不幸,这样还是没有定位我出现问题的代码.
最终方案
首先说一下 EXC_BAD_ACCESS 这个错误,可以这么说,90%的错误来源在于对一个已经释放的对象进行release操作.那么我们应该启用 僵尸对象.方法如下:
Product->Scheme->Edit Scheme->Arguments 的 Environment Variables 中,增加标计位NSZombieEnabled设为YES)
objc
这样,就能看到崩溃的具体原因了.但是如果想知道代码,这个原文说需要借助 Xcode 控制台的 GDB,很不幸的是,高版本的 Xcode 中,已经没有切换到 GDB 的功能了.网上也有帖子介绍 GDB 对应 lldb 的指令是什么,很不幸,都不能正常工作,所以这里不在罗列.
后来,我想到是否可以借助终端来完成任务?
那么问题来了,终端怎么知道这个内存地址是属于谁的?
后来想到,可以通过活动监视器,拿到我们程序进程的 pid
我的是 1175
然后参考刚刚原文中的指令, sudo malloc_history 1175 0x7a692620
后面一个参数是崩溃的地址.
此时终端提示,没有打印 MallocStackLogging
.
其实这里是需要在 Xocde 中配置的.按照刚刚配置 NSZombieEnabled
的位置和方式,添加一个参数:
MallocStackLoggingNoCompact
值设置为 YES
然后继续重新编译运行,查看 pid ,查看崩溃的内存地址.
见到终端打印出了调用顺序,一般来说,是最后一个你自定义的方法导致的崩溃.
然后就是解决 bug 了,祝好运 ~
参考网址:
http://blog.csdn.net/totogo2010/article/details/8949440
http://mobile.51cto.com/iphone-279455.htm